package com.detection.tcp.server;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.thread.ThreadUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dahuatech.icc.exception.ClientException;
import com.dahuatech.icc.oauth.utils.HttpUtils;
import com.detection.common.config.DetectionConfig;
import com.detection.common.domain.SysDictData;
import com.detection.common.domain.SysUser;
import com.detection.common.domain.SysUserAuth;
import com.detection.common.utils.spring.SpringUtils;
import com.detection.dahua.config.OauthConfigUtil;
import com.detection.dahua.demo.accesscontrol.auth.AuthDemoTest;
import com.detection.dahua.demo.brm.person.PersonDemo;
import com.detection.dahua.demo.video.realTimePreview.RealTimePreviewDemo;
import com.detection.dahua.model.accesscontrol.record.QueryRecordRequest;
import com.detection.dahua.model.accesscontrol.record.QueryRecordResponse;
import com.detection.dahua.model.brm.device.DevicePageRequest;
import com.detection.dahua.model.brm.device.DevicePageResponse;
import com.detection.dahua.model.brm.device.DeviceTreeRequest;
import com.detection.dahua.model.brm.device.DeviceTreeResponse;
import com.detection.dahua.model.video.realTimePreview.HlsUrlRequest;
import com.detection.dahua.model.video.realTimePreview.HlsUrlResponse;
import com.detection.dahua.model.video.videoReplay.HlsPlaybackRequest;
import com.detection.dahua.model.video.videoReplay.HlsPlaybackResponse;
import com.detection.dahua.model.video.videoReplay.RegularVideoRecordRequest;
import com.detection.dahua.model.video.videoReplay.RegularVideoRecordResponse;
import com.detection.dahua.service.*;
import com.detection.hikvision.domain.CameraPreviewResultView;
import com.detection.hikvision.service.AccessControlService;
import com.detection.hikvision.service.ArtemisCamerasService;
import com.detection.tcp.client.NettyTcpClient;
import com.detection.tcp.domain.TCPMessage;
import com.detection.tcp.utils.CameraDetectionStatusUtils;
import com.detection.tcp.variable.TcpMessageVariable;
import com.detection.tcp.variable.YoloVariables;
import com.detection.yolo.service.CameraDetectionService;
import com.detection.yolo.service.FfmpegRecordVideoService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author chenlei
 */
@Slf4j
public class NettyTcpServerHandler extends ChannelInboundHandlerAdapter {

    /**
     * 保存连接到服务端的通道信息，对连接的客户端进行管理，包括连接的添加、删除、查找等操作。
     */
    private static final ConcurrentHashMap<ChannelId, ChannelHandlerContext> CHANNEL_MAP = new ConcurrentHashMap<>();


    /**
     * 当有客户端连接到服务器时，此方法会被触发。
     * 它会记录客户端的 IP 地址、端口号以及连接的 ChannelId，并将该连接添加到 CHANNEL_MAP 中。
     * 如果连接已经存在于 CHANNEL_MAP 中，会打印相应的日志信息；如果不存在，则添加到映射中并记录连接信息。
     *
     * @param ctx 通道处理器上下文，包含了通道的信息和操作通道的方法
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 获取客户端的网络地址信息
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        // 获取客户端的 IP 地址
        String clientIp = insocket.getAddress().getHostAddress();
        // 获取客户端的端口号
        int clientPort = insocket.getPort();
        // 获取连接通道的唯一标识
        ChannelId channelId = ctx.channel().id();

        // 如果该连接通道已经在映射中，打印连接状态信息
        if (CHANNEL_MAP.containsKey(channelId)) {
            log.info("客户端【" + channelId + "】是连接状态，连接通道数量: " + CHANNEL_MAP.size());
        } else {
            // 将新的连接添加到映射中
            CHANNEL_MAP.put(channelId, ctx);
            log.info("客户端【" + channelId + "】连接 netty 服务器[IP:" + clientIp + "--->PORT:" + clientPort + "]");
            log.info("连接通道数量: " + CHANNEL_MAP.size());
        }
    }


    /**
     * 当有客户端终止连接服务器时，此方法会被触发。
     * 它会从 CHANNEL_MAP 中移除该客户端的连接信息，并打印相应的退出信息和更新后的连接通道数量。
     * 首先检查该连接是否存在于 CHANNEL_MAP 中，如果存在则进行移除操作。
     *
     * @param ctx 通道处理器上下文
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = insocket.getAddress().getHostAddress();
        ChannelId channelId = ctx.channel().id();
        // 检查映射中是否包含该客户端连接
        if (CHANNEL_MAP.containsKey(channelId)) {
            // 从映射中移除连接
            CHANNEL_MAP.remove(channelId);
            log.info("客户端【" + channelId + "】退出 netty 服务器[IP:" + clientIp + "--->PORT:" + insocket.getPort() + "]");
            log.info("连接通道数量: " + CHANNEL_MAP.size());
        }
    }


    /**
     * 当有客户端向服务器发送消息时，此方法会被触发。
     * 它会打印接收到的客户端消息，并调用 channelWrite 方法将消息返回给客户端。
     * 首先会打印接收到客户端报文的日志信息，然后调用 channelWrite 方法进行响应。
     *
     * @param ctx 通道处理器上下文
     * @param msg 从客户端接收到的消息对象
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            String message = "";
            if (msg instanceof ByteBuf) {
                ByteBuf byteBuf = (ByteBuf) msg;
                // 将 ByteBuf 转换为字符串
                message = byteBuf.toString(CharsetUtil.UTF_8);
                log.info("接收到的内容: {}", message);
            } else if (msg instanceof String) {
                message = (String) msg;
                log.info("接收到的内容: {}", message);
            } else {
                log.warn("未知类型的消息: {}", msg.getClass().getName());
            }
            JSONObject jsonObject = JSON.parseObject(message);
            String flag = jsonObject.getString("flag");


            IDahuaAccessService dahuaAccessService = SpringUtils.getBean(IDahuaAccessService.class);
            IDahuaDeviceService dahuaDeviceService = SpringUtils.getBean(IDahuaDeviceService.class);
            IDahuaRealTimePreviewService dahuaRealTimePreviewService = SpringUtils.getBean(IDahuaRealTimePreviewService.class);
            IDahuaUserService dahuaUserService = SpringUtils.getBean(IDahuaUserService.class);
            IDahuaVideoReplayService dahuaVideoReplayService = SpringUtils.getBean(IDahuaVideoReplayService.class);

            RealTimePreviewDemo realTimePreviewDemo = SpringUtils.getBean(RealTimePreviewDemo.class);

            CameraDetectionService cameraDetectionService = SpringUtils.getBean(CameraDetectionService.class);
            FfmpegRecordVideoService ffmpegRecordVideoService = SpringUtils.getBean(FfmpegRecordVideoService.class);
            AccessControlService accessControlService = SpringUtils.getBean(AccessControlService.class);

            NettyTcpClient nettyTcpClient = new NettyTcpClient();
            //如果是一体机启动执行下一步，执行完后退出
            if (DetectionConfig.isDeployOneMachineEnabled()) {
                nettyTcpClient.sendMessage(TCPMessage.builder()
                        .flag(jsonObject.getString("flag"))
                        .id(jsonObject.getString("id"))
                        .data(jsonObject.getString("data"))
                        .campCode(DetectionConfig.getCampCode())
                        .build());
                return;
            }
            switch (flag) {
                case "controlDoorOpen":
                    List<String> controlDoorOpen = JSONObject.parseObject(jsonObject.getString("data"), List.class);
                    switch (DetectionConfig.getIOTplatform()){
                        case "dahua":
                            dahuaAccessService.controlDoorOpen(controlDoorOpen);
                            break;
                        case "hikvision":
                            String[] stringArray = controlDoorOpen.toArray(new String[0]);
                            accessControlService.getAccessControlDoControl(stringArray, 2);
                            break;
                    }
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("controlDoorOpen")
                            .id(jsonObject.getString("id"))
                            .data("true")
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "controlDoorClose":
                    List<String> controlDoorClose = JSONObject.parseObject(jsonObject.getString("data"), List.class);
                    switch (DetectionConfig.getIOTplatform()){
                        case "dahua":
                            dahuaAccessService.controlDoorClose(controlDoorClose);
                            break;
                        case "hikvision":
                            String[] stringArray = controlDoorClose.toArray(new String[0]);
                            accessControlService.getAccessControlDoControl(stringArray, 1);
                            break;
                    }
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("controlDoorClose")
                            .id(jsonObject.getString("id"))
                            .data("true")
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "controlDoorAlwaysOpen":
                    List<String> controlDoorAlwaysOpen = JSONObject.parseObject(jsonObject.getString("data"), List.class);
                    switch (DetectionConfig.getIOTplatform()){
                        case "dahua":
                            dahuaAccessService.controlDoorAlwaysOpen(controlDoorAlwaysOpen);
                            break;
                        case "hikvision":
                            String[] stringArray = controlDoorAlwaysOpen.toArray(new String[0]);
                            accessControlService.getAccessControlDoControl(stringArray, 0);
                            break;
                    }
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("controlDoorAlwaysOpen")
                            .id(jsonObject.getString("id"))
                            .data("true")
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "controlDoorAlwaysClose":
                    List<String> controlDoorAlwaysClose = JSONObject.parseObject(jsonObject.getString("data"), List.class);
                    switch (DetectionConfig.getIOTplatform()){
                        case "dahua":
                            dahuaAccessService.controlDoorAlwaysClose(controlDoorAlwaysClose);
                            break;
                        case "hikvision":
                            String[] stringArray = controlDoorAlwaysClose.toArray(new String[0]);
                            accessControlService.getAccessControlDoControl(stringArray, 3);
                            break;
                    }
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("controlDoorAlwaysClose")
                            .id(jsonObject.getString("id"))
                            .data("true")
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getHlsUrl":
                    HlsUrlRequest getHlsUrl = JSONObject.parseObject(jsonObject.getString("data"), HlsUrlRequest.class);
                    HlsUrlResponse hlsUrl = dahuaRealTimePreviewService.getHlsUrl(getHlsUrl);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getHlsUrl")
                            .id(jsonObject.getString("id"))
                            .data(hlsUrl)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getFFmpegRecordHlsUrl":
                    HlsUrlRequest getHlsUrl1 = JSONObject.parseObject(jsonObject.getString("data"), HlsUrlRequest.class);
                    String cameraIndexCode = getHlsUrl1.getData().getChannelId();
                    if(CameraDetectionStatusUtils.ffmpegRecordProcessMap.containsKey(cameraIndexCode)){
                        JSONObject result = new JSONObject();
                        result.put("netUrlPath",CameraDetectionStatusUtils.ffmpegRecordNetPathMap.get(cameraIndexCode));
                        String urlPath = "/hls/"+cameraIndexCode+"/"+cameraIndexCode+".m3u8";
                        result.put("urlPath",urlPath);
                        TCPMessage message1 = TCPMessage.builder()
                                .flag("getFFmpegRecordHlsUrl")
                                .id(jsonObject.getString("id"))
                                .data(result)
                                .campCode(DetectionConfig.getCampCode())
                                .build();
                        nettyTcpClient.sendMessage(message1);
                        return;
                    }
                    String ffmpetHlsUrl = null;
                    switch (DetectionConfig.getIOTplatform()){
                        case "dahua":
                            HlsUrlResponse hlsUrlResponse = dahuaRealTimePreviewService.getHlsUrl(getHlsUrl1);
                            String token1 = HttpUtils.getToken(OauthConfigUtil.getOauthConfig()).getAccess_token();
                            String hlsPath = hlsUrlResponse.getData().getUrl();
                            if (!StringUtils.isEmpty(token1)) {
                                hlsPath += "?token=" + token1;
                            }
                            hlsPath = parseAndResolveUrl(hlsPath, OauthConfigUtil.getOauthConfig().getHttpConfigInfo().getHost());
                            ffmpetHlsUrl = ffmpegRecordVideoService.getHlsUrl(hlsPath,getHlsUrl1.getData().getChannelId());
                            break;
                        case "hikvision":
                            ArtemisCamerasService artemisCamerasService = SpringUtils.getBean(ArtemisCamerasService.class);
                            CameraPreviewResultView cameraPreviewUrls1 = artemisCamerasService.getCameraPreviewUrls(cameraIndexCode, 1, "rtsp", 1);
                            String pathUrl = cameraPreviewUrls1.getData().getUrl();
                            ffmpetHlsUrl = ffmpegRecordVideoService.getHlsUrl(pathUrl,cameraIndexCode);
                            break;
                    }
                    JSONObject result = new JSONObject();
                    result.put("netUrlPath",ffmpetHlsUrl);
                    String urlPath = "/hls/"+cameraIndexCode+"/"+cameraIndexCode+".m3u8";
                    result.put("urlPath",urlPath);
                    CameraDetectionStatusUtils.ffmpegRecordNetPathMap.put(cameraIndexCode,ffmpetHlsUrl);

                    ThreadUtil.execAsync(new Runnable() {
                        @Override
                        public void run() {
                            TCPMessage message1 = TCPMessage.builder()
                                    .flag("getFFmpegRecordHlsUrl")
                                    .id(jsonObject.getString("id"))
                                    .data(result)
                                    .campCode(DetectionConfig.getCampCode())
                                    .build();
                            long count = 0;
                            while (true){
                                File file = new File(DetectionConfig.getProfile()+urlPath);
                                if(file.exists()){
                                    break;
                                }
                                if(count>=20){
                                    break;
                                }
                                count++;
                                try {
                                    Thread.sleep(1000);
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                            nettyTcpClient.sendMessage(message1);
                        }
                    });

                    break;
                case "stopCameraPreviewUrlVideo":
                    String stopIndexCode = jsonObject.getString("data");
                    if("-1".equals(stopIndexCode)){
                        Map<String,Process> processMap = CameraDetectionStatusUtils.ffmpegRecordProcessMap;
                        processMap.forEach((indexCode,process)->{
                            process.destroy();
                        });
                        FileUtil.del(DetectionConfig.getProfile()+"/hls/");
                        CameraDetectionStatusUtils.ffmpegRecordProcessMap.clear();
                        CameraDetectionStatusUtils.ffmpegRecordNetPathMap.clear();
                    }
                    if(!"-1".equals(stopIndexCode)){
                       Process process = CameraDetectionStatusUtils.ffmpegRecordProcessMap.get(stopIndexCode);
                       process.destroy();
                       CameraDetectionStatusUtils.ffmpegRecordProcessMap.remove(stopIndexCode);
                       CameraDetectionStatusUtils.ffmpegRecordNetPathMap.remove(stopIndexCode);
                       FileUtil.del(DetectionConfig.getProfile()+"/hls/"+stopIndexCode);
                    }

                    break;
                case "getRegularVideoRecords":
                    RegularVideoRecordRequest getRegularVideoRecords = JSONObject.parseObject(jsonObject.getString("data"), RegularVideoRecordRequest.class);
                    RegularVideoRecordResponse regularVideoRecords = dahuaVideoReplayService.getRegularVideoRecords(getRegularVideoRecords);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getRegularVideoRecords")
                            .id(jsonObject.getString("id"))
                            .data(regularVideoRecords)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getPlayBackHlsUrl":
                    HlsPlaybackRequest getPlayBackHlsUrl = JSONObject.parseObject(jsonObject.getString("data"), HlsPlaybackRequest.class);
                    HlsPlaybackResponse playBackHlsUrl = dahuaVideoReplayService.getPlayBackHlsUrl(getPlayBackHlsUrl);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getPlayBackHlsUrl")
                            .id(jsonObject.getString("id"))
                            .data(playBackHlsUrl)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getRecordPage":
                    QueryRecordRequest getRecordPage = JSONObject.parseObject(jsonObject.getString("data"), QueryRecordRequest.class);
                    QueryRecordResponse recordPage = dahuaAccessService.getRecordPage(getRecordPage);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getRecordPage")
                            .id(jsonObject.getString("id"))
                            .data(recordPage)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getRecordCount":
                    QueryRecordRequest getRecordCount = JSONObject.parseObject(jsonObject.getString("data"), QueryRecordRequest.class);
                    Integer recordCount = dahuaAccessService.getRecordCount(getRecordCount);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getRecordCount")
                            .id(jsonObject.getString("id"))
                            .data(recordCount)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getDeviceTree":
                    DeviceTreeRequest getDeviceTree = JSONObject.parseObject(jsonObject.getString("data"), DeviceTreeRequest.class);
                    DeviceTreeResponse deviceTree = dahuaDeviceService.getDeviceTree(getDeviceTree);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getDeviceTree")
                            .id(jsonObject.getString("id"))
                            .data(deviceTree)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getDeviceTreeSync":
                    DeviceTreeRequest getDeviceTreeSync = JSONObject.parseObject(jsonObject.getString("data"), DeviceTreeRequest.class);
                    dahuaDeviceService.getDeviceTreeSync(getDeviceTreeSync);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getDeviceTreeSync")
                            .id(jsonObject.getString("id"))
                            .data("true")
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getDevicePage":
                    DevicePageRequest getDevicePage = JSONObject.parseObject(jsonObject.getString("data"), DevicePageRequest.class);
                    DevicePageResponse devicePage = dahuaDeviceService.getDevicePage(getDevicePage);
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getDevicePage")
                            .id(jsonObject.getString("id"))
                            .data(devicePage)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getDeviceOnlineStatus":
                    Integer[] deviceOnlineStatus = dahuaDeviceService.getDeviceOnlineStatus();
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getDeviceOnlineStatus")
                            .id(jsonObject.getString("id"))
                            .data(deviceOnlineStatus)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "getToken":
                    String token = HttpUtils.getToken(OauthConfigUtil.getOauthConfig()).getAccess_token();
                    nettyTcpClient.sendMessage(TCPMessage.builder()
                            .flag("getToken")
                            .id(jsonObject.getString("id"))
                            .data(token)
                            .campCode(DetectionConfig.getCampCode())
                            .build());
                    break;
                case "personNum":
                    YoloVariables.pensonNum = JSONObject.parseObject(jsonObject.getString("data"), Integer.class);
                    break;
                case "importantCarrierCameraName":
                    YoloVariables.importantCarrierCameraName = JSON.parseArray(jsonObject.getString("data"), SysDictData.class);
                    break;
                case "materialsEquipmentCameraName":
                    YoloVariables.materialsEquipmentCameraName = JSON.parseArray(jsonObject.getString("data"), SysDictData.class);
                    break;
                case "startDetectionVideo":
                    JSONObject dataObj = jsonObject.getJSONObject("data");
                    String cameraName = dataObj.getString("cameraName");
                    String channelId = dataObj.getString("channelId");
                    String streamType = dataObj.getString("streamType");
                    String type = dataObj.getString("type");
                    String supportAlgorithms = dataObj.getString("supportAlgorithms");
//                    startDahuaDevice(channelId, cameraName, channelId, streamType, type, supportAlgorithms);
                    if("hikvision".equals(DetectionConfig.getIOTplatform())){
                        startHaiKangDevice(channelId,cameraName,supportAlgorithms);
                    }else if("dahua".equals(DetectionConfig.getIOTplatform())){
                        startDahuaDevice(channelId, cameraName, channelId, streamType, type, supportAlgorithms);
                    }
                    break;
                case "firstStartStatus":
                    if (!CameraDetectionStatusUtils.SoftFirstStartStatus.get("firstStartStatus")) {
                        CameraDetectionStatusUtils.SoftFirstStartStatus.replace("firstStartStatus", true);
                        JSONArray firstStartStatusArrays = jsonObject.getJSONArray("data");
                        for (int i = 0; i < firstStartStatusArrays.size(); i++) {
                            JSONObject oneObj = firstStartStatusArrays.getJSONObject(i);
                            String indexCode = oneObj.getString("indexCode");
                            String name = oneObj.getString("name");
                            String supportAlgorithms1 = oneObj.getString("supportAlgorithms");
//                            startDahuaDevice(indexCode, name, indexCode, "2", "hls", supportAlgorithms1);
                            if("hikvision".equals(DetectionConfig.getIOTplatform())){
                                startHaiKangDevice(indexCode,name,supportAlgorithms1);
                            }else if("dahua".equals(DetectionConfig.getIOTplatform())){
                                startDahuaDevice(indexCode, name, indexCode, "2", "hls", supportAlgorithms1);
                            }
                            Thread.sleep(200);
                        }
                    }

                    break;
                case "stopDetectionVideo":
                    JSONObject stopDetectionVideoObj = jsonObject.getJSONObject("data");
                    String indexCode = stopDetectionVideoObj.getString("indexCode");
                    CameraDetectionStatusUtils.CameraDetectionStatusMap.replace(indexCode, true);
                    break;
                case "personAuth":
                    JSONObject personAuth = jsonObject.getJSONObject("data");
                    SysUserAuth sysUser = personAuth.toJavaObject(SysUserAuth.class);
                    PersonDemo personDemo = new PersonDemo();
                    personDemo.addPerson(sysUser);
                    Thread.sleep(5000);
                    AuthDemoTest authDemoTest = new AuthDemoTest();
                    List<String> personCodes = new ArrayList<>();
                    personCodes.add("afsystem"+sysUser.getUserId().toString());
                    authDemoTest.testPersonAuth(personCodes,sysUser.getAccessIndexCode());
                    break;
                default:
                    String id = jsonObject.getString("id");
                    if (!StringUtils.isEmpty(id)) {
                        TcpMessageVariable.tcpMessageConcurrentHashMap.put(id, JSON.parseObject(message, TCPMessage.class));
                    }
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            ReferenceCountUtil.release(msg); // 安全释放资源
        }
    }
    private void startHaiKangDevice(String indexCode, String cameraName, String supportAlgorithms){
        ArtemisCamerasService artemisCamerasService = SpringUtils.getBean(ArtemisCamerasService.class);
        CameraDetectionService cameraDetectionService = SpringUtils.getBean(CameraDetectionService.class);
        CameraPreviewResultView cameraPreviewUrls = artemisCamerasService.getCameraPreviewUrls(indexCode, 1, "rtsp", 1);
        String url = cameraPreviewUrls.getData().getUrl();
        cameraDetectionService.startDetection(indexCode, cameraName, url, supportAlgorithms);
    }
    private void startDahuaDevice(String indexCode, String cameraName, String channelId, String streamType, String type, String supportAlgorithms) throws ClientException {
        RealTimePreviewDemo realTimePreviewDemo = SpringUtils.getBean(RealTimePreviewDemo.class);
        CameraDetectionService cameraDetectionService = SpringUtils.getBean(CameraDetectionService.class);
        String token = HttpUtils.getToken(OauthConfigUtil.getOauthConfig()).getAccess_token();
        HlsUrlRequest hlsUrlRequest = new HlsUrlRequest();
        HlsUrlRequest.Data data = new HlsUrlRequest.Data();
        data.setChannelId(channelId);
        data.setStreamType(streamType);
        data.setType(type);
        hlsUrlRequest.setData(data);
        HlsUrlResponse hlsUrlResponse = realTimePreviewDemo.getHlsUrl(hlsUrlRequest);
        String hlsPath = hlsUrlResponse.getData().getUrl();
        if (!StringUtils.isEmpty(token)) {
            hlsPath += "?token=" + token;
        }
        if (Objects.isNull(cameraName)) {
            cameraName = channelId;
        }

        hlsPath = parseAndResolveUrl(hlsPath, OauthConfigUtil.getOauthConfig().getHttpConfigInfo().getHost());
        cameraDetectionService.startDetection(indexCode, cameraName, hlsPath, supportAlgorithms);
    }

    public static void main(String[] args) {
        System.out.println(OauthConfigUtil.getOauthConfig().getHttpConfigInfo().getHost());
    }
    /**
     * 解析大華可用的視頻地址
     * @param urlList
     * @param targetIp
     * @return
     */
    private static String parseAndResolveUrl(String urlList, String targetIp) {
        if (urlList == null || urlList.trim().isEmpty()) {
            throw new IllegalArgumentException("URL列表不能为空");
        }
        if (targetIp == null || targetIp.trim().isEmpty()) {
            throw new IllegalArgumentException("目标IP不能为空");
        }

        String[] urls = urlList.split("\\|");
        // 去除空白字符串
        urls = Arrays.stream(urls)
                .filter(s -> !s.trim().isEmpty())
                .toArray(String[]::new);

        if (urls.length == 0) {
            throw new IllegalArgumentException("未解析到有效URL");
        }

        // 遍历每个 URL，检查其 IP 是否等于目标 IP
        for (String url : urls) {
            try {
                URI uri = new URI(url.trim());
                String host = uri.getHost();
                if (targetIp.equals(host)) {
                    return url.trim(); // 找到匹配的 IP，直接返回该 URL
                }
            } catch (URISyntaxException e) {
                throw new IllegalArgumentException("无效的URL格式: " + url, e);
            }
        }

        // 如果没有找到匹配的 URL，则修改第一个 URL 的 IP
        try {
            URI firstUri = new URI(urls[0].trim());
            URI updatedUri = new URI(
                    firstUri.getScheme(),
                    firstUri.getUserInfo(),
                    targetIp, // 替换为指定 IP
                    firstUri.getPort(),
                    firstUri.getPath(),
                    firstUri.getQuery(),
                    firstUri.getFragment()
            );
            return updatedUri.toString();
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("第一个URL格式不正确: " + urls[0], e);
        }
    }

    /**
     * 服务端给客户端发送消息的方法。
     * 首先根据传入的 ChannelId 从 CHANNEL_MAP 中获取对应的 ChannelHandlerContext，
     * 然后检查消息是否为空以及 ChannelHandlerContext 是否存在，若存在则将消息写入通道并刷新缓冲区。
     *
     * @param channelId 连接通道的唯一标识
     * @param msg       需要发送的消息内容
     */
    public void channelWrite(ChannelId channelId, Object msg) {
        // 获取与 ChannelId 对应的 ChannelHandlerContext
        ChannelHandlerContext ctx = CHANNEL_MAP.get(channelId);
        if (ctx == null) {
            log.info("通道【" + channelId + "】不存在");
            return;
        }
        if (msg == null || msg == "") {
            log.info("服务端响应空的消息");
            return;
        }
        // 将消息写入通道
        ctx.write(msg);
        // 刷新通道的输出缓冲区，确保消息被发送出去
        ctx.flush();
    }


    /**
     * 当触发用户事件时，此方法会被调用，主要用于处理空闲状态事件。
     * 根据不同的空闲状态（读、写、总超时）进行相应的处理，如断开连接。
     * 首先检查触发的事件是否是 IdleStateEvent，如果是则判断具体的空闲状态并进行相应处理。
     *
     * @param ctx 通道处理器上下文
     * @param evt 触发的事件对象
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        String socketString = ctx.channel().remoteAddress().toString();
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.info("Client: " + socketString + " READER_IDLE 读超时");
                // 读超时，断开连接
                ctx.disconnect();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                log.info("Client: " + socketString + " WRITER_IDLE 写超时");
                // 写超时，断开连接
                ctx.disconnect();
            } else if (event.state() == IdleState.ALL_IDLE) {
                log.info("Client: " + socketString + " ALL_IDLE 总超时");
                // 总超时，断开连接
                ctx.disconnect();
            }
        }
    }


    /**
     * 当发生异常时，此方法会被触发。
     * 它会关闭通道并打印相应的错误信息，同时打印当前的连接通道数量。
     * 异常发生时，会关闭通道以防止资源泄漏，并且打印异常发生的通道信息和当前的连接数量。
     *
     * @param ctx   通道处理器上下文
     * @param cause 引发异常的原因
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
        log.info(ctx.channel().id() + " 发生了错误,此连接被关闭" + "此时连通数量: " + CHANNEL_MAP.size());
    }
}
